from collections import deque
from comment.modules.debt_info import Debt_info
from comment.modules.matched_result import Matched_result
from comment.modules.expected_return import Expected_return
from sqlalchemy import or_,and_
from comment.modules.funding_not_matched import Funding_not_matched
from comment.modules import db
from flask import current_app
from comment.utils.gen_trans_id import gen_trans_id
from financial.resource.transaction.const import DealType
from datetime import datetime
from dateutil.relativedelta import relativedelta
from flask_restful import Resource
class MatchQueue:


    def __init__(self,item_list):
        '''
        初始化栈。禁用deque的右端，使用左端
        '''

        self.items = deque(item_list)

    #从栈中取一条待匹配的数据
    def get_item(self):
        return self.items.popleft()

    #将一条即将待匹配的数据  放入栈中
    def push(self,item):
        return self.items.appendleft(item)

    #判断一个栈的数据是否为空
    @property
    def empty(self):
        return len(self.items) == 0

class Match(object):
    '''
    正式开始撮合匹配资金
    '''

    def __init__(self):
        '''初始化，把所有待匹配的债权和未匹配的资金，都从数据库中取出来'''
        #查询未匹配或者部分匹配的债权列表
        debt_list = Debt_info.query.filter(or_(Debt_info.matchedStatus == 0, Debt_info.matchedStatus == 1)).order_by(Debt_info.id).all()
        self.debtDeque = MatchQueue(debt_list) #待匹配的债权  栈

        #未匹配或部分匹配资金列表
        fund_list = Funding_not_matched.query.filter(or_(Funding_not_matched.matchedStatus == 0, Funding_not_matched.matchedStatus == 1)).order_by(Funding_not_matched.fId).all()
        self.fundsDeque =MatchQueue(fund_list)


    def start_match(self):
        '''正式开始撮合匹配'''
        try:
            while (not self.fundsDeque.empty) and (not self.debtDeque.empty):
                debt_item = self.debtDeque.get_item()
                fund_item = self.fundsDeque.get_item()
            #匹配有三种情况，债权资金金额  大于  理财金额 ,  第二种，债权资金金额  小于  理财金额 ,第三种，债权资金金额  等于  理财金额 ,
            if debt_item.debtMoney > fund_item.fNotMatchedMoney:
                self.debtMoneyMoreThanFunds(debt_item,fund_item)
            elif debt_item.debtMoney > fund_item.fNotMatchedMoney:
                self.debtMoneyLessThanFunds(debt_item, fund_item)
            else:
                self.debtMoneyEqualThanFunds(debt_item,fund_item)

            db.session.commit()
            return {'msg':'匹配完成'}
        except Exception as e:
            current_app.logger.error(e)
            db.session.rollback()
            return {'message' : '服务器忙，请稍后重试'},500

    #  债权资金部分匹配的业务逻辑: 第一：债权资金金额 大于 理财金额
    def debtMoneyMoreThanFunds(self,debt,funds):
        #新增一条匹配结果
        self.createMatchedResult(debt , funds , funds.fNotMatchedMoney)
        debt.debtMoney -= funds.fNotMatchedMoney  #债权剩下未匹配金额
        debt.matchedStatus = 1  # 修改债权的匹配状态为1
        debt.matchedMoney += funds.fNotMatchedMoney  # 修改债权的已经匹配金额

        funds.fNotMatchedMoney = 0
        funds.matchedStatus = 2  #修改债权的匹配金额为2
        #债权还剩下一点资金没有匹配完，马上插入重新到栈
        self.debtDeque.push(debt)

        #投资的理财全部匹配完了，开始产生收益
        self.createExpectedResult(funds)

    def debtMoneyLessThanFunds(self,debt,funds):
        '''待匹配的 债权资金 小于  投资理财的资金'''
        funds.fNotMatchedMoney -= debt.debtMoney #投资资剩余金未匹配
        funds.matchedStatus = 1
        self.createMatchedResult(debt,funds,debt.debtMoney)
        debt.matchedMoney += debt.debtMoney  #修改债权中已经匹配的金额
        debt.debtMoney = 0  #匹配完成之后，修改债权的待匹配金额
        debt.user.accountInfo.balance   += debt.repaymenMoney
        debt.matchedStatus = 2
        self.startRepayPlay(debt)  # 正式开始还款计划
        self.fundsDeque.push(funds)  # 把剩余没有匹配的资金放入栈中

    ## 第三种： 投资资金 等于 债权的资金
    def debtMoneyEqualThanFunds(self,debt ,funds):
        debt.matchedStatus = 2  # 债权匹配状态 0未匹配 1部分 2完全匹配
        debt.matchedMoney += debt.debtMoney  #修改债权中已经匹配的金额
        self.createMatchedResult(debt,funds,debt.debtMoney)
        debt.debtMoney = 0
        funds.fNotMatchedMoney = 0
        funds.matchedStatus = 2
        self.createExpectedResult(funds)
        self.startRepayPlay(debt)



    def startRepayPlay(self,debt):
        '''正式开始还款计划'''
        repay_list = debt.debtor_record
        for i in range(len(repay_list)):
            repay_day = datetime.now() + relativedelta(months=i+1)
            # 修改还款计划中还款时间
            repay_list[i].receivableDate =repay_day


    def createMatchedResult(self,debt,funds,match_money):
        #创建一条匹配结果数据，并保存到数据库中
        match_num = gen_trans_id(DealType.match.name)
        result = Matched_result(userId=funds.investRecord.pUid, debtId=debt.id, investId=funds.investRecord.pId,
                                transferSerialNo=match_num, purchaseMoney=match_money,
                                confirmedDate=funds.investRecord.pDate,
                                isConfirmed=1, matchDate=datetime.now())
        db.session.add(result)


    def createExpectedResult(self,funds):
        """创建一条预期收益的记录"""
        ex_return = Expected_return(userId=funds.investRecord.pUid, productId=funds.investRecord.pProductId,
                                    investRecord=funds.investRecord.pId,
                                    expectedDate=datetime.now() + relativedelta(months=funds.investRecord.pDeadline),
                                    expectedMoney=funds.investRecord.pProspectiveEarnings)
        funds.investRecord.pStatus = 1
        # 修改 投资记录的开始计息时间
        funds.investRecord.pInterestStartDate = datetime.now()
        db.session.add(ex_return)


class MatchUp(Resource):

    def post(self):
        match = Match()
        return match.start_match()